package com.tools.middle.redis;

import com.tools.common.object.Note;
import com.tools.common.object.string.VerifyKit;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;
import java.util.LinkedHashSet;

/**
 * Redis 客户端实例的构建工厂工具类
 * */
@Note("Redis 客户端实例的构建工厂工具类")
public final class RedisKit {

    private RedisKit() {}

    /* **********************************************************************************
     *
     *          Jedis 连接池/ Jedis 集群配置
     *
     * *********************************************************************************
     * */

    @Note("【单节点连接池配置对象】：根据实体的配置项构建一个 Redis 的连接池配置对象 JedisPoolConfig")
    public static JedisPoolConfig newJedisPoolConfig(RedisSource source) {
        if(source == null) throw new NullPointerException("Redis 配置项包装实体为 null");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(source.getMaxTotal());
        jedisPoolConfig.setMaxIdle(source.getMaxIdle());
        jedisPoolConfig.setMinIdle(source.getMinIdle());
        jedisPoolConfig.setMaxWait(Duration.ofMillis(source.getMaxWait()));
        return jedisPoolConfig;
    }

    @Note("根据配置和节点的主机、端口构建一个 Jedis 连接池实例")
    public static JedisPool newJedisPool(RedisSource source) {
        JedisPoolConfig config = newJedisPoolConfig(source);
        HostAndPort node = source.firstNode();
        int socketTimeout = source.getSocketTimeout();
        String clientName = source.getClientName();
        if(source.usernameNotEmpty()) {
            String username = source.getUsername();
            String password = source.getPassword();
            int databaseIndex = source.getDatabaseIndex();
            int connectionTimeout = source.getConnectionTimeout();
            return new JedisPool(config, node.getHost(), node.getPort(), connectionTimeout, socketTimeout, username, password, databaseIndex, clientName);
        }
        if(source.passwordNotEmpty()) {
            String password = source.getPassword();
            int databaseIndex = source.getDatabaseIndex();
            int connectionTimeout = source.getConnectionTimeout();
            return new JedisPool(config, node.getHost(), node.getPort(), connectionTimeout, socketTimeout, password, databaseIndex, clientName);
        }
        return new JedisPool(config, node.getHost(), node.getPort(), socketTimeout, clientName);
    }


    @Note("根据配置和节点的主机、端口构建一个 JedisCluster 集群客户端实例")
    public static JedisCluster newJedisCluster(RedisSource source) {
        JedisPoolConfig config = newJedisPoolConfig(source);
        LinkedHashSet<HostAndPort> hostAndPortSet = source.getHostAndPortSet();
        int socketTimeout = source.getSocketTimeout();
        int connectionTimeout = source.getConnectionTimeout();
        int maxAttempts = source.getMaxAttempts();
        if(source.usernameNotEmpty()) {
            String username = source.getUsername();
            String password = source.getPassword();
            String clientName = source.getClientName();
            return new JedisCluster(hostAndPortSet, connectionTimeout, socketTimeout, maxAttempts, username, password, clientName, config);
        }
        if(source.passwordNotEmpty()) {
            String password = source.getPassword();
            String clientName = source.getClientName();
            return new JedisCluster(hostAndPortSet, connectionTimeout, socketTimeout, maxAttempts, password, clientName, config);
        }
        return new JedisCluster(hostAndPortSet, connectionTimeout, socketTimeout, maxAttempts, config);
    }


    /* **********************************************************************************
     *
     *          其他方法
     *
     * *********************************************************************************
     * */

    @Note("将 【IP:端口】 的字符串构建为 HostAndPort 实例对象")
    public static HostAndPort newHostAndPort(String hostAndPort) {
        if(VerifyKit.isHostAndPort(hostAndPort)) {
            String[] array = hostAndPort.split(":");
            int port = Integer.parseInt(array[1]);
            return new HostAndPort(array[0], port);
        }
        throw new IllegalArgumentException("Redis 节点 【" + hostAndPort + "】 不是标准的 【ip:端口号】 格式字符串");
    }

    @Note("将 IP/主机 和端口构建为 HostAndPort 实例对象")
    public static HostAndPort toHostAndPort(String host, int port) {
        String hostAndPorts = host + ":" + port;
        if(VerifyKit.isHostAndPort(hostAndPorts)) {
            return new HostAndPort(host, port);
        }
        throw new IllegalArgumentException("Redis 节点 【" + hostAndPorts + "】 不是标准的 【ip:端口号】 格式字符串");
    }


    /* **********************************************************************************
     *
     *          Spring-Data-Redis 的方法
     *
     * *********************************************************************************
     * */

    @Note("返回指定泛型的 RedisTemplate 对象。RedisConnectionFactory 对象可以从 Spring 容器中获取")
    public static <K, V, HK, HV> RedisTemplate<K, V> newRedisTemplate(RedisConnectionFactory factory,
                                                              RedisSerializer<K> keySerializer, RedisSerializer<V> valueSerializer,
                                                              RedisSerializer<HK> hashKeySerializer, RedisSerializer<HV> hashValueSerializer) {
        RedisTemplate<K, V> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(keySerializer);
        redisTemplate.setHashKeySerializer(hashKeySerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setHashValueSerializer(hashValueSerializer);
        return redisTemplate;
    }


    @Note("返回 key-value 都是字符串类型的 RedisTemplate 对象。RedisConnectionFactory 对象可以从 Spring 容器中获取")
    public static RedisTemplate<String, String> newStringRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setHashValueSerializer(valueSerializer);
        return redisTemplate;
    }

    @Note("返回标准类型的 RedisTemplate 对象。RedisConnectionFactory 对象可以从 Spring 容器中获取")
    public static RedisTemplate<String, Object> newStandardRedisTemplate(RedisConnectionFactory factory) {
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();
        return newRedisTemplate(factory, keySerializer, valueSerializer, keySerializer, valueSerializer);
    }

    @Note("从 RedisConnectionFactory Redis 连接工厂中获取 Redis 节点连接对象")
    public static RedisConnection newRedisConnection(RedisConnectionFactory factory) {
        return factory.getConnection();
    }
}
